#include <iostream>
#include <sstream>
#include <string>
#include <stdio.h>
#include <time.h>
#include <google/protobuf/message.h>
#include <google/protobuf/descriptor.h>
#include "../proto/database.pb.h"
#include "bufferDB.h"
#include <boost/asio.hpp>

using namespace std;
using boost::asio::ip::tcp;
using namespace google::protobuf;

bufferDB::bufferDB(void) {
   stringData = "UnInitialized";
   floatData = 0.00;
   int32Data = 65535;
   EntryNum = 65535;
   boolData = false;
   msg = new db::Database();
   msgDescript = msg->GetDescriptor();
   msgReflect = msg->GetReflection();
   thestream = NULL;
}

void bufferDB::setNewMessage(db::Database* inputMsg)
{
   msg = inputMsg;
   msgDescript = msg->GetDescriptor();
   msgReflect = msg->GetReflection();	
}

//This is a pointer to a repeated message
void bufferDB::setRepeatedMsgPtr(int repeatedMsgNum)
{
   cout<<"Repeated Field "<<repeatedMsgNum<<" is at Field Number: "<<repeatedFieldMap[repeatedMsgNum]<<endl;
   msgField = msgDescript->field(repeatedFieldMap[repeatedMsgNum]);
}

//This is a pointer to a repeated nested Message
//This can be initialized after you setRepeatedMsgPtr
//As that function sets the msgField pointer to the repeated message
void bufferDB::AddNestedMsgElement()
{
   if (msgField->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) 
   {
      if (msgField->is_repeated()) 
      {		  
         nestedMsg = msgReflect->AddMessage(msg, msgField);
         nestedReflect = nestedMsg->GetReflection();
      } 
      else 
      {
         printf("Field is NOT repeated and should NOT be set using this method.");
      }
   }		

}

void bufferDB::setFieldPtr(int fieldNum)
{
   msgField = msgDescript->field(fieldNum);
}

void bufferDB::setNestedTypePtr(int nestedFieldNum)
{
   nestedDescript = msgDescript->nested_type(nestedFieldNum);
}

void bufferDB::setNestedTokenTypePtr(int nestedTokenNum)
{
   nestedField = nestedDescript->field(nestedTokenNum);	
}

void bufferDB::setMessageEntryPtr(int num)
{
   EntryNum=num;
}

int bufferDB::findRepeatedFields()
{
   int repeatedFieldMax=0;

   for(int i=0;i<msgDescript->field_count();i++)
   {
      msgField = msgDescript->field(i);
      if(msgField->is_repeated())
      {
         repeatedFieldMap[repeatedFieldMax] = i;
         ++repeatedFieldMax;
      }
   }

   return repeatedFieldMax;
}

int bufferDB::findNestedTypes()
{	
   return msgDescript->nested_type_count();
}

int bufferDB::findNestedTokenTypes()
{	
   return nestedDescript->field_count();
}

int bufferDB::getFieldCnt()
{
   return msgDescript->field_count();
}

int bufferDB::getEntryCnt()
{
   return msgReflect->FieldSize(*msg, msgField);
}
string bufferDB::getNestedMessageName()
{
   return nestedDescript->name();
}

string bufferDB::getNestedTokenName()
{
   return nestedField->name();
}

FieldDescriptor::CppType bufferDB::getFieldType()
{
   return msgField->cpp_type();
}

FieldDescriptor::CppType bufferDB::getNestedTokenType()
{
   return nestedField->cpp_type();
}


void bufferDB::getSingleFieldData()
{		
   if(msgField->cpp_type()==FieldDescriptor::CPPTYPE_BOOL)
   {
      boolData = msgReflect->GetBool(*msg, msgField);  	
   }
   else if(msgField->cpp_type()==FieldDescriptor::CPPTYPE_STRING)
   {
      stringData = msgReflect->GetString(*msg, msgField);			  			
   }
   else if(msgField->cpp_type()==FieldDescriptor::CPPTYPE_INT32)
   {
      int32Data = msgReflect->GetInt32(*msg, msgField);
   }
   else if(msgField->cpp_type()==FieldDescriptor::CPPTYPE_FLOAT)
   {
      floatData = msgReflect->GetFloat(*msg, msgField);  
   }
   else if(msgField->cpp_type()==FieldDescriptor::CPPTYPE_DOUBLE)
   {
      doubleData = msgReflect->GetDouble(*msg, msgField);
   }											
}

void bufferDB::setSingleFieldFromString(string token)
{
   if(msgField->cpp_type()==FieldDescriptor::CPPTYPE_BOOL)
   {
      bool boolData = (token == "true"?true:false); 
      msgReflect->SetBool(msg, msgField, boolData); 	
   }
   else if(msgField->cpp_type()==FieldDescriptor::CPPTYPE_STRING)
   {
      string stringData = token;
      msgReflect->SetString(msg, msgField, stringData);			  			
   }
   else if(msgField->cpp_type()==FieldDescriptor::CPPTYPE_INT32)
   {
      int int32Data = atoi(token.c_str());
      msgReflect->SetUInt32(msg, msgField, int32Data); 
   }
   else if(msgField->cpp_type()==FieldDescriptor::CPPTYPE_FLOAT)
   {
      istringstream fD(token);
      float floatData;
      fD>>floatData;
      msgReflect->SetFloat(msg, msgField, floatData); 
   }
   else if(msgField->cpp_type()==FieldDescriptor::CPPTYPE_DOUBLE)
   {
      istringstream fD(token);
      double doubleData;
      fD>>doubleData;
      msgReflect->SetDouble(msg, msgField, doubleData);
      

   }										
}



void bufferDB::getRepeatedFieldData(int repeatedCnt)
{
   if(msgField->cpp_type()==FieldDescriptor::CPPTYPE_BOOL)
   {
      boolData = msgReflect->GetRepeatedBool(*msg, msgField, repeatedCnt);  	
   }
   else if(msgField->cpp_type()==FieldDescriptor::CPPTYPE_STRING)
   {
      stringData = msgReflect->GetRepeatedString(*msg, msgField, repeatedCnt);			  			
   }
   else if(msgField->cpp_type()==FieldDescriptor::CPPTYPE_INT32)
   {
      int32Data = msgReflect->GetRepeatedInt32(*msg, msgField, repeatedCnt);  
   }
   else if(msgField->cpp_type()==FieldDescriptor::CPPTYPE_FLOAT)
   {
      floatData = msgReflect->GetRepeatedFloat(*msg, msgField, repeatedCnt);
   }
   else if(msgField->cpp_type()==FieldDescriptor::CPPTYPE_DOUBLE)
   {
      doubleData = msgReflect->GetRepeatedDouble(*msg, msgField, repeatedCnt);
   }											
}

void bufferDB::setRepeatedFieldFromString(string token, int repeatedIndex)
{
   if(msgField->cpp_type()==FieldDescriptor::CPPTYPE_BOOL)
   {
      bool boolData = (token == "true"?true:false); 
      msgReflect->SetRepeatedBool(msg, msgField, boolData, repeatedIndex);
   }
   else if(msgField->cpp_type()==FieldDescriptor::CPPTYPE_STRING)
   {
      string stringData = token;
      nestedReflect->SetRepeatedString(msg, msgField, repeatedIndex, stringData);
   }
   else if(msgField->cpp_type()==FieldDescriptor::CPPTYPE_INT32)
   {
      int int32Data = atoi(token.c_str());
      msgReflect->SetRepeatedUInt32(msg, msgField, int32Data, repeatedIndex);
      
      
   }
   else if(msgField->cpp_type()==FieldDescriptor::CPPTYPE_FLOAT)
   {
      istringstream fD(token);
      float floatData;
      fD>>floatData;
      msgReflect->SetRepeatedFloat(msg, msgField, floatData, repeatedIndex);      
   }
   else if(msgField->cpp_type()==FieldDescriptor::CPPTYPE_DOUBLE, repeatedIndex)
   {
      istringstream fD(token);
      double doubleData;
      fD>>doubleData;
      msgReflect->SetRepeatedDouble(msg, msgField, doubleData, repeatedIndex);
    }										
}


bool bufferDB::getNestedBool()
{
   if(msgReflect->GetRepeatedMessage(*msg, msgField, EntryNum).IsInitialized())
      nestedEntryReflect = msgReflect->GetRepeatedMessage(*msg, msgField, EntryNum).GetReflection();
   else
      cout<<"DOH Message not initialized"<<endl;

   if(nestedField->cpp_type()==FieldDescriptor::CPPTYPE_BOOL)
   {
      boolData = nestedEntryReflect->GetBool(msgReflect->GetRepeatedMessage(*msg, msgField, EntryNum), nestedField);
      
      
      return boolData;	
   }
   else
      cout<<"You did not correctly set the NestedTokenTypePtr, this is a bool."<<endl;	
}

string bufferDB::getNestedString()
{
   if(msgReflect->GetRepeatedMessage(*msg, msgField, EntryNum).IsInitialized())
      nestedEntryReflect = msgReflect->GetRepeatedMessage(*msg, msgField, EntryNum).GetReflection();
   else
      cout<<"DOH Message not initialized"<<endl;


   if(nestedField->cpp_type()==FieldDescriptor::CPPTYPE_STRING)
   {
      stringData = nestedEntryReflect->GetString(msgReflect->GetRepeatedMessage(*msg, msgField, EntryNum), nestedField);			
      
      
   }
   else
      cout<<"You did not correctly set the NestedTokenTypePtr, this is a string."<<endl;

   return stringData;
}

int32 bufferDB::getNestedInt32()
{
   if(msgReflect->GetRepeatedMessage(*msg, msgField, EntryNum).IsInitialized())

      nestedEntryReflect = msgReflect->GetRepeatedMessage(*msg, msgField, EntryNum).GetReflection();
   else
      cout<<"DOH Message not initialized"<<endl;


   if(nestedField->cpp_type()==FieldDescriptor::CPPTYPE_INT32)
   {
      int32Data = nestedEntryReflect->GetInt32(msgReflect->GetRepeatedMessage(*msg, msgField, EntryNum), nestedField);
   }
   else
      cout<<"You did not correctly set the NestedTokenTypePtr, this is a int32."<<endl;

   return int32Data;
}

float bufferDB::getNestedFloat()	
{
   if(msgReflect->GetRepeatedMessage(*msg, msgField, EntryNum).IsInitialized())
      nestedEntryReflect = msgReflect->GetRepeatedMessage(*msg, msgField, EntryNum).GetReflection();
   else
      cout<<"DOH Message not initialized"<<endl;


   if(nestedField->cpp_type()==FieldDescriptor::CPPTYPE_FLOAT)
   {
      floatData = nestedEntryReflect->GetFloat(msgReflect->GetRepeatedMessage(*msg, msgField, EntryNum), nestedField);
   }
   else
      cout<<"You did not correctly set the NestedTokenTypePtr, this is a float."<<endl;

   return floatData;

}


double bufferDB::getNestedDouble()
{
   if(msgReflect->GetRepeatedMessage(*msg, msgField, EntryNum).IsInitialized())
      nestedEntryReflect = msgReflect->GetRepeatedMessage(*msg, msgField, EntryNum).GetReflection();
   else
      cout<<"DOH Message not initialized"<<endl;


   if(nestedField->cpp_type()==FieldDescriptor::CPPTYPE_DOUBLE)
   {
      doubleData = nestedEntryReflect->GetDouble(msgReflect->GetRepeatedMessage(*msg, msgField, EntryNum), nestedField);
   }
   else
      cout<<"You did not correctly set the NestedTokenTypePtr, this is a double."<<endl;												

   return doubleData;
}

void bufferDB::setNestedSingleFieldFromString(string token)
{
   if(nestedField->cpp_type()==FieldDescriptor::CPPTYPE_BOOL)
   {
      bool boolData = (token == "true"?true:false);			 
      nestedReflect->SetBool(nestedMsg, nestedField, boolData);
   }
   else if(nestedField->cpp_type()==FieldDescriptor::CPPTYPE_STRING)
   {
      string stringData = token;		
      nestedReflect->SetString(nestedMsg, nestedField, stringData);						
   }
   else if(nestedField->cpp_type()==FieldDescriptor::CPPTYPE_INT32)
   {
      int int32Data = atoi(token.c_str());
      nestedReflect->SetUInt32(nestedMsg, nestedField, int32Data);

   }
   else if(nestedField->cpp_type()==FieldDescriptor::CPPTYPE_FLOAT)
   {
      istringstream fD(token);
      float floatData;
      fD>>floatData;			
      nestedReflect->SetFloat(nestedMsg, nestedField, floatData);
   }
   else if(nestedField->cpp_type()==FieldDescriptor::CPPTYPE_DOUBLE)
   {
      istringstream fD(token);
      double doubleData;
      fD>>doubleData;	
      nestedReflect->SetDouble(nestedMsg, nestedField, doubleData);
   }
   else
      cout<<"This type did not match any of the allowable types, the type is "<<nestedField->cpp_type()<<endl;										
}

void bufferDB::getNestedRepeatedFieldData(int nestedRepeatedIndex)
{
   if(nestedField->cpp_type()==FieldDescriptor::CPPTYPE_BOOL)
   {
      boolData = nestedReflect->GetRepeatedBool(*nestedMsg, nestedField, nestedRepeatedIndex);
   }
   else if(nestedField->cpp_type()==FieldDescriptor::CPPTYPE_STRING)
   {
      stringData = nestedReflect->GetRepeatedString(*nestedMsg, nestedField, nestedRepeatedIndex);		
   }
   else if(nestedField->cpp_type()==FieldDescriptor::CPPTYPE_INT32)
   {
      int32Data = nestedReflect->GetRepeatedInt32(*nestedMsg, nestedField, nestedRepeatedIndex);
   }
   else if(nestedField->cpp_type()==FieldDescriptor::CPPTYPE_FLOAT)
   {
      floatData = nestedReflect->GetRepeatedFloat(*nestedMsg, nestedField, nestedRepeatedIndex);
   }
   else if(nestedField->cpp_type()==FieldDescriptor::CPPTYPE_DOUBLE)
   {
      doubleData = nestedReflect->GetRepeatedDouble(*nestedMsg, nestedField, nestedRepeatedIndex);
   }											
}

void bufferDB::setNestedRepeatedFromString(string token, int nestedRepeatedIndex)
{
   if(nestedField->cpp_type()==FieldDescriptor::CPPTYPE_BOOL)
   {
      bool boolData = (token == "true"?true:false); 
      nestedReflect->SetRepeatedBool(nestedMsg, nestedField, boolData, nestedRepeatedIndex);
   }
   else if(nestedField->cpp_type()==FieldDescriptor::CPPTYPE_STRING)
   {
      string stringData = token;			
      nestedReflect->SetRepeatedString(nestedMsg, nestedField, nestedRepeatedIndex, stringData);		
   }
   else if(nestedField->cpp_type()==FieldDescriptor::CPPTYPE_INT32)
   {
      int int32Data = atoi(token.c_str());
      nestedReflect->SetRepeatedInt32(nestedMsg, nestedField, int32Data, nestedRepeatedIndex);
   }
   else if(nestedField->cpp_type()==FieldDescriptor::CPPTYPE_FLOAT)
   {
      istringstream fD(token);
      float floatData;
      fD>>floatData;
      nestedReflect->SetRepeatedFloat(nestedMsg, nestedField, floatData, nestedRepeatedIndex);
   }
   else if(nestedField->cpp_type()==FieldDescriptor::CPPTYPE_DOUBLE, nestedRepeatedIndex)
   {
      istringstream fD(token);
      double doubleData;
      fD>>doubleData;
      nestedReflect->SetRepeatedDouble(nestedMsg, nestedField, doubleData, nestedRepeatedIndex);
   }										
}

void bufferDB::setWrite(bool writeVal)
{
   msg->set_write(writeVal);     
}

void bufferDB::serializeAndSend(char* argument)
{
   if (thestream == NULL) {
      thestream = new tcp::iostream(argument, "daytime");
   }

   try
   {
      cout << "serializeAndSend(" << argument << ")" << endl;	

      if (!msg->SerializeToOstream(thestream)) {
         cout << "Error serializing stream output!" << endl;
      } else {
         cout << "Stream serialized" << endl;
      }

      thestream->close();
   }
   catch (std::exception& e)
   {
      std::cout << "Exception: " << e.what() << std::endl;
   }	

   cout.flush();
}

void bufferDB::serializeAndListen(char* argument)
{
   if (thestream == NULL) {
      thestream = new tcp::iostream(argument, "daytime");
   }

   try
   {
      cout << "serializeAndListen(" << argument << ")" << endl;

      if (!msg->SerializeToOstream(thestream)) {
         cout<<"Error serializing stream output!"<<endl;
      } else {
         cout<<"Stream serialized"<<endl;
      }

      thestream->close();
   }
   catch (std::exception& e)
   {
      std::cout << "Exception: " << e.what() << std::endl;
   }	

   cout.flush();
}

string bufferDB::getFieldName(void)
{
   return msgField->name();
}


